﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;


namespace advadev.CanOpen
{

    /// <summary>
    /// Represents a service data object upload transfer
    /// </summary>
    public class ServiceDataObjectUploadTransfer : ITransfer
    {

        /// <summary>for internal use</summary>
        private int _numberOfUploadedBytes;
        /// <summary>for internal use</summary>
        private int _numberOfBytesToBeUploaded;
        /// <summary>for internal use</summary>
        private int _numberOfBytesNotContainingData;
        /// <summary>for internal use</summary>
        private int _toggleBit;
        /// <summary>for internal use</summary>
        private int _sequenceNumber;
        /// <summary>for internal use</summary>
        private int _nextBlockUploadClientSubCommand;
        /// <summary>for internal use</summary>
        private bool _clientCheckSumSupport;
        /// <summary>for internal use</summary>
        private bool _serverCheckSumSupport;
        /// <summary>for internal use</summary>
        private bool _blockTransferEndSend;
        /// <summary>for internal use</summary>
        private bool _isTransferCompleteAfterSending;
        /// <summary>for internal use</summary>
        private ServiceDataObjectTransferType _transferType;
        /// <summary>for internal use</summary>
        private CanOpenMessage _receivedCanOpenMessage;
        /// <summary>for internal use</summary>
        private ICanOpenObjectDictionaryContainer _canOpenObjectDictionaryContainer;
        /// <summary>for internal use</summary>
        private Collection<CanOpenMessage> _pendingSendMessages = new Collection<CanOpenMessage>();
        /// <summary>for internal use</summary>
        private ReadOnlyCollection<CanOpenMessage> _pendingSendMessagesReference;
        /// <summary>for internal use</summary>
        static private bool _isCheckSumSupported = false;
        /// <summary>
        /// Default send COB-ID
        /// </summary>
        static public readonly UInt16 DefaultClientCobId = 0x600;
        /// <summary>
        /// Default receive COB-ID
        /// </summary>
        static public readonly UInt16 DefaultServerCobId = 0x580;
        /// <summary>
        /// Default receive COB-ID
        /// </summary>
        static public readonly byte DefaultBlockSize = 127;
        /// <summary>
        /// Default receive COB-ID
        /// </summary>
        static public readonly byte DefaultProtocolSwitchThreshold = 28;


        /// <summary>
        /// Initializes a new instance of the ServiceDataObjectUploadTransfer
        /// class by the given parameter
        /// </summary>
        /// <param name="transferType">
        /// Transfer type
        /// </param>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <param name="canOpenObjectDictionaryContainer">
        /// Can open object dictionary container
        /// </param>
        /// <param name="nodeId">
        /// Node id of the device to upload the object from (must be in the
        /// range of 1..127)
        /// </param>
        /// <param name="clientCobId">
        /// Client COB-ID
        /// </param>
        /// <param name="serverCobId">
        /// Server COB-ID
        /// </param>
        private ServiceDataObjectUploadTransfer(ServiceDataObjectTransferType transferType, CanOpenMessage receivedCanOpenMessage, ICanOpenObjectDictionaryContainer canOpenObjectDictionaryContainer, byte nodeId, UInt16 clientCobId, UInt16 serverCobId)
        {
            _transferType = transferType;
            _receivedCanOpenMessage = receivedCanOpenMessage;
            _canOpenObjectDictionaryContainer = canOpenObjectDictionaryContainer;
            NodeId = nodeId;
            ClientCobId = clientCobId;
            ServerCobId = serverCobId;
            ByteArray = new List<byte>();
        }

        /// <summary>
        /// Initializes a new instance of the ServiceDataObjectUploadTransfer
        /// class by the given parameter
        /// </summary>
        /// <param name="canOpenValueObject">
        /// Can open value object for which to create a upload transfer
        /// </param>
        /// <param name="nodeId">
        /// Node id of the device to upload the object from (must be in the
        /// range of 1..127)
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Will be thrown if the can open value object is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Will be thrown if the node id is not in the range of 1..127
        /// </exception>
        public ServiceDataObjectUploadTransfer(ICanOpenValueObject canOpenValueObject, byte nodeId)
            : this(canOpenValueObject, nodeId, ServiceDataObjectTransferMode.Auto, DefaultClientCobId, DefaultServerCobId, DefaultBlockSize, DefaultProtocolSwitchThreshold)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ServiceDataObjectUploadTransfer
        /// class by the given parameter
        /// </summary>
        /// <param name="canOpenValueObject">
        /// Can open value object for which to create a upload transfer
        /// </param>
        /// <param name="nodeId">
        /// Node id of the device to upload the object from (must be in the
        /// range of 1..127)
        /// </param>
        /// <param name="preferredTransferMode">
        /// Preferred transfer mode to use (only if possible)
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Will be thrown if the can open value object is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Will be thrown if the node id is not in the range of 1..127
        /// </exception>
        public ServiceDataObjectUploadTransfer(ICanOpenValueObject canOpenValueObject, byte nodeId, ServiceDataObjectTransferMode preferredTransferMode)
            : this(canOpenValueObject, nodeId, preferredTransferMode, DefaultClientCobId, DefaultServerCobId, DefaultBlockSize, DefaultProtocolSwitchThreshold)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ServiceDataObjectUploadTransfer
        /// class by the given parameter
        /// </summary>
        /// <param name="canOpenValueObject">
        /// Can open value object for which to create a upload transfer
        /// </param>
        /// <param name="nodeId">
        /// Node id of the device to upload the object from (must be in the
        /// range of 1..127)
        /// </param>
        /// <param name="preferredTransferMode">
        /// Preferred transfer mode to use (only if possible)
        /// </param>
        /// <param name="blockSize">
        /// Block size to use for block transfer
        /// </param>
        /// <param name="protocolSwitchThreshold">
        /// Protocol switch threshold for block transfer
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Will be thrown if the can open value object is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Will be thrown if the node id is not in the range of 1..127 or the
        /// block size is not in the range 1..127
        /// </exception>
        public ServiceDataObjectUploadTransfer(ICanOpenValueObject canOpenValueObject, byte nodeId, ServiceDataObjectTransferMode preferredTransferMode, byte blockSize, byte protocolSwitchThreshold)
            : this(canOpenValueObject, nodeId, preferredTransferMode, DefaultClientCobId, DefaultServerCobId, blockSize, protocolSwitchThreshold)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ServiceDataObjectUploadTransfer
        /// class by the given parameter
        /// </summary>
        /// <param name="canOpenValueObject">
        /// Can open value object for which to create a upload transfer
        /// </param>
        /// <param name="nodeId">
        /// Node id of the device to upload the object from (must be in the
        /// range of 1..127)
        /// </param>
        /// <param name="clientCobId">
        /// Client COB-ID
        /// </param>
        /// <param name="serverCobId">
        /// Server COB-ID
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Will be thrown if the can open value object is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Will be thrown if the node id is not in the range of 1..127
        /// </exception>
        public ServiceDataObjectUploadTransfer(ICanOpenValueObject canOpenValueObject, byte nodeId, UInt16 clientCobId, UInt16 serverCobId)
            : this(canOpenValueObject, nodeId, ServiceDataObjectTransferMode.Auto, clientCobId, serverCobId, DefaultBlockSize, DefaultProtocolSwitchThreshold)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ServiceDataObjectUploadTransfer
        /// class by the given parameter
        /// </summary>
        /// <param name="canOpenValueObject">
        /// Can open value object for which to create a upload transfer
        /// </param>
        /// <param name="nodeId">
        /// Node id of the device to upload the object from (must be in the
        /// range of 1..127)
        /// </param>
        /// <param name="preferredTransferMode">
        /// Preferred transfer mode to use (only if possible)
        /// </param>
        /// <param name="clientCobId">
        /// Client COB-ID
        /// </param>
        /// <param name="serverCobId">
        /// Server COB-ID
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Will be thrown if the can open value object is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Will be thrown if the node id is not in the range of 1..127
        /// </exception>
        public ServiceDataObjectUploadTransfer(ICanOpenValueObject canOpenValueObject, byte nodeId, ServiceDataObjectTransferMode preferredTransferMode, UInt16 clientCobId, UInt16 serverCobId)
            : this(canOpenValueObject, nodeId, preferredTransferMode, clientCobId, serverCobId, DefaultBlockSize, DefaultProtocolSwitchThreshold)
        {
        }

        /// <summary>
        /// Initializes a new instance of the ServiceDataObjectUploadTransfer
        /// class by the given parameter
        /// </summary>
        /// <param name="canOpenValueObject">
        /// Can open value object for which to create a upload transfer
        /// </param>
        /// <param name="nodeId">
        /// Node id of the device to upload the object from (must be in the
        /// range of 1..127)
        /// </param>
        /// <param name="preferredTransferMode">
        /// Preferred transfer mode to use (only if possible)
        /// </param>
        /// <param name="clientCobId">
        /// Client COB-ID
        /// </param>
        /// <param name="serverCobId">
        /// Server COB-ID
        /// </param>
        /// <param name="blockSize">
        /// Block size to use for block transfer
        /// </param>
        /// <param name="protocolSwitchThreshold">
        /// Protocol switch threshold for block transfer
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// Will be thrown if the can open value object is null
        /// </exception>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Will be thrown if the node id is not in the range of 1..127 or the
        /// block size is not in the range 1..127
        /// </exception>
        public ServiceDataObjectUploadTransfer(ICanOpenValueObject canOpenValueObject, byte nodeId, ServiceDataObjectTransferMode preferredTransferMode, UInt16 clientCobId, UInt16 serverCobId, byte blockSize, byte protocolSwitchThreshold)
        {
            if (canOpenValueObject == null)
            {
                throw new ArgumentNullException("canOpenValueObject");
            }
            if (!(1 <= nodeId && nodeId <= 127))
            {
                throw new ArgumentOutOfRangeException("nodeId", "The node id must be in the range of 1..127");
            }
            if (!(1 <= blockSize && blockSize <= 127))
            {
                throw new ArgumentOutOfRangeException("blockSize", "The block size must be in the range of 1..127");
            }
            CanOpenValueObject = canOpenValueObject;
            NodeId = nodeId;
            ClientCobId = clientCobId;
            ServerCobId = serverCobId;
            BlockSize = blockSize;
            ProtocolSwitchThreshold = protocolSwitchThreshold;
            ByteArray = new List<byte>();
            if ((canOpenValueObject.IsBlockUploadTransferAdvised && preferredTransferMode != ServiceDataObjectTransferMode.SegmentedTransfer) || preferredTransferMode == ServiceDataObjectTransferMode.BlockTransfer)
            {
                _transferType = ServiceDataObjectTransferType.ClientSideBlockTransfer;
            }
            else
            {
                _transferType = ServiceDataObjectTransferType.ClientSideSegmentedTransfer;
            }
        }


        /// <summary>
        /// Gets or sets the can open value object
        /// </summary>
        private ICanOpenValueObject CanOpenValueObject
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the node id of the device to upload the object from
        /// </summary>
        private byte NodeId
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the client COB-ID
        /// </summary>
        private UInt16 ClientCobId
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the server COB-ID
        /// </summary>
        private UInt16 ServerCobId
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the block size
        /// </summary>
        private byte BlockSize
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the protocol switch threshold
        /// </summary>
        private byte ProtocolSwitchThreshold
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the byte array
        /// </summary>
        private IList<byte> ByteArray
        {
            get;
            set;
        }


        /// <summary>
        /// Checks if the given received can open message is a SDO upload
        /// transfer request and if so it returns as out parameter a matching
        /// SDO upload transfer instance
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <param name="canOpenObjectDictionaryContainer">
        /// Can open object dictionary container
        /// </param>
        /// <param name="serviceDataObjectUploadTransfer">
        /// A matching SDO upload transfer instance
        /// </param>
        /// <returns>
        /// True if the received can open message is a SDO upload transfer
        /// request, false otherwise
        /// </returns>
        static public bool IsServiceDataObjectUploadTransferRequest(CanOpenMessage receivedCanOpenMessage, ICanOpenObjectDictionaryContainer canOpenObjectDictionaryContainer, out ServiceDataObjectUploadTransfer serviceDataObjectUploadTransfer)
        {
            return IsServiceDataObjectUploadTransferRequest(receivedCanOpenMessage, canOpenObjectDictionaryContainer, out serviceDataObjectUploadTransfer, DefaultClientCobId, DefaultServerCobId);
        }

        /// <summary>
        /// Checks if the given received can open message is a SDO upload
        /// transfer request and if so it returns as out parameter a matching
        /// SDO upload transfer instance
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <param name="canOpenObjectDictionaryContainer">
        /// Can open object dictionary container
        /// </param>
        /// <param name="serviceDataObjectUploadTransfer">
        /// A matching SDO upload transfer instance
        /// </param>
        /// <param name="clientCobId">
        /// Client COB-ID
        /// </param>
        /// <param name="serverCobId">
        /// Server COB-ID
        /// </param>
        /// <returns>
        /// True if the received can open message is a SDO upload transfer
        /// request, false otherwise
        /// </returns>
        static public bool IsServiceDataObjectUploadTransferRequest(CanOpenMessage receivedCanOpenMessage, ICanOpenObjectDictionaryContainer canOpenObjectDictionaryContainer, out ServiceDataObjectUploadTransfer serviceDataObjectUploadTransfer, UInt16 clientCobId, UInt16 serverCobId)
        {
            // check parameter
            if (receivedCanOpenMessage == null || canOpenObjectDictionaryContainer == null)
            {
                serviceDataObjectUploadTransfer = null;
                return false;
            }

            // check client command specifier
            byte x = (byte)((receivedCanOpenMessage.Byte0 & 0x18) >> 3);
            if (x != 0)
            {
                serviceDataObjectUploadTransfer = null;
                return false;
            }
            byte clientCommandSpecifier = (byte)((receivedCanOpenMessage.Byte0 & 0xE0) >> 5);
            byte clientSubCommand = (byte)(receivedCanOpenMessage.Byte0 & 0x3);
            if (!(clientCommandSpecifier == (byte)ClientCommandSpecifier.InitiateUploadRequest ||
                (clientCommandSpecifier == (byte)ClientCommandSpecifier.BlockUpload && clientSubCommand == (byte)ClientSubCommand.InitiateRequest)))
            {
                serviceDataObjectUploadTransfer = null;
                return false;
            }

            // check node id
            UInt16 nodeId = receivedCanOpenMessage.CanId;
            nodeId -= clientCobId;
            if (!(1 <= nodeId && nodeId <= 127))
            {
                serviceDataObjectUploadTransfer = null;
                return false;
            }

            // create transfer instance
            if (clientCommandSpecifier == (byte)ClientCommandSpecifier.InitiateUploadRequest)
            {
                serviceDataObjectUploadTransfer = new ServiceDataObjectUploadTransfer(ServiceDataObjectTransferType.ServerSideSegmentedTransfer, receivedCanOpenMessage, canOpenObjectDictionaryContainer, (byte)nodeId, clientCobId, serverCobId);
                return true;
            }
            else if (clientCommandSpecifier == (byte)ClientCommandSpecifier.BlockUpload)
            {
                serviceDataObjectUploadTransfer = new ServiceDataObjectUploadTransfer(ServiceDataObjectTransferType.ServerSideBlockTransfer, receivedCanOpenMessage, canOpenObjectDictionaryContainer, (byte)nodeId, clientCobId, serverCobId);
                return true;
            }
            serviceDataObjectUploadTransfer = null;
            return false;
        }

        /// <summary>
        /// Starts a client side transfer
        /// </summary>
        private void startClientSideTransfer()
        {
            CanOpenValueObject.IsValueChangedEventRaisingPaused = true;
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Clear();
            switch (_transferType)
            {
                case ServiceDataObjectTransferType.ClientSideSegmentedTransfer:
                    startSegmentedTransfer();
                    break;
                case ServiceDataObjectTransferType.ClientSideBlockTransfer:
                    startBlockTransfer();
                    break;
            }
        }

        /// <summary>
        /// Starts a server side transfer
        /// </summary>
        private void startServerSideTransfer()
        {
            // initialization
            UInt16 canId = ServerCobId;
            canId += NodeId;

            // find value object
            UInt16 index = (UInt16)(_receivedCanOpenMessage.Byte1 + (_receivedCanOpenMessage.Byte2 << 8));
            byte subIndex = _receivedCanOpenMessage.Byte3;
            CanOpenValueObject = _canOpenObjectDictionaryContainer.GetCanOpenObject(index, subIndex);
            if (CanOpenValueObject == null)
            {
                byte serverCommandSpecifier = (byte)ServerCommandSpecifier.AbortTransferRequest;
                byte byte0 = (byte)(serverCommandSpecifier << 5);
                byte byte1 = (byte)(index & 0x00FF);
                byte byte2 = (byte)((index & 0xFF00) >> 8);
                byte byte3 = subIndex;
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x00, 0x00, 0x02, 0x06));
                AbortCode = new CanOpenAbortCode(0x00, 0x00, 0x02, 0x06);
                _isTransferCompleteAfterSending = true;
                return;
            }

            // check if value object is readable
            CanOpenValueObject.IsValueChangedEventRaisingPaused = true;
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Clear();
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_receivedCanOpenMessage);
            if (!CanOpenValueObject.IsReadable)
            {
                byte serverCommandSpecifier = (byte)ServerCommandSpecifier.AbortTransferRequest;
                byte byte0 = (byte)(serverCommandSpecifier << 5);
                byte byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                byte byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                byte byte3 = CanOpenValueObject.SubIndex;
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x01, 0x00, 0x01, 0x06));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                AbortCode = new CanOpenAbortCode(0x01, 0x00, 0x01, 0x06);
                _isTransferCompleteAfterSending = true;
                return;
            }

            // initalize and check data
            if (_transferType == ServiceDataObjectTransferType.ServerSideBlockTransfer)
            {
                CanOpenValueObject.ByteOrder = CanOpenByteOrder.BigEndian;
            }
            else
            {
                CanOpenValueObject.ByteOrder = CanOpenByteOrder.LittleEndian;
            }
            ByteArray = CanOpenValueObject.ToByteArray();
            CanOpenValueObject.ByteOrder = CanOpenByteOrder.LittleEndian;
            if (ByteArray.Count == 0)
            {
                TransferComplete = (PendingSendMessages.Count == 0);
                return;
            }
            if (_transferType == ServiceDataObjectTransferType.ServerSideBlockTransfer)
            {
                ProtocolSwitchThreshold = _receivedCanOpenMessage.Byte5;
                if (ByteArray.Count <= ProtocolSwitchThreshold)
                {
                    CanOpenValueObject.ByteOrder = CanOpenByteOrder.LittleEndian;
                    ByteArray = CanOpenValueObject.ToByteArray();
                    _transferType = ServiceDataObjectTransferType.ServerSideSegmentedTransfer;
                }
            }
            if (ByteArray.Count <= 4 && _transferType == ServiceDataObjectTransferType.ServerSideSegmentedTransfer)
            {
                _transferType = ServiceDataObjectTransferType.ServerSideExpeditedTransfer;
            }

            // process received message
            switch (_transferType)
            {
                case ServiceDataObjectTransferType.ServerSideExpeditedTransfer:
                    initializeServerSideUploadExpeditedTransfer(_receivedCanOpenMessage, canId);
                    break;
                case ServiceDataObjectTransferType.ServerSideSegmentedTransfer:
                    initializeServerSideUploadSegmentedTransfer(_receivedCanOpenMessage, canId);
                    break;
                case ServiceDataObjectTransferType.ServerSideBlockTransfer:
                    initializeServerSideUploadBlockTransfer(_receivedCanOpenMessage, canId);
                    break;
            }
        }

        /// <summary>
        /// Initializes a server side upload expedited transfer by the given
        /// parameter
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <param name="canId">
        /// CAN-ID
        /// </param>
        private void initializeServerSideUploadExpeditedTransfer(CanOpenMessage receivedCanOpenMessage, UInt16 canId)
        {
            byte serverCommandSpecifier = (byte)ServerCommandSpecifier.InitiateUploadResponse;
            byte numberOfBytesNotContainingData = (byte)(4 - ByteArray.Count);
            byte transferType = 1;
            byte sizeIndicator = 1;
            byte byte0 = (byte)((serverCommandSpecifier << 5) | (numberOfBytesNotContainingData << 2) | (transferType << 1) | sizeIndicator);
            byte byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
            byte byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
            byte byte3 = CanOpenValueObject.SubIndex;
            byte byte4 = (ByteArray.Count > 0 ? ByteArray[0] : (byte)0);
            byte byte5 = (ByteArray.Count > 1 ? ByteArray[1] : (byte)0);
            byte byte6 = (ByteArray.Count > 2 ? ByteArray[2] : (byte)0);
            byte byte7 = (ByteArray.Count > 3 ? ByteArray[3] : (byte)0);
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            _isTransferCompleteAfterSending = true;
        }

        /// <summary>
        /// Starts a segmented transfer
        /// </summary>
        private void startSegmentedTransfer()
        {
            UInt16 canId = ClientCobId;
            canId += NodeId;
            byte clientCommandSpecifier = (byte)ClientCommandSpecifier.InitiateUploadRequest;
            byte byte0 = (byte)(clientCommandSpecifier << 5);
            byte byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
            byte byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
            byte byte3 = CanOpenValueObject.SubIndex;
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x00));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
        }

        /// <summary>
        /// Initializes a server side upload segmented transfer by the given
        /// parameter
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <param name="canId">
        /// CAN-ID
        /// </param>
        private void initializeServerSideUploadSegmentedTransfer(CanOpenMessage receivedCanOpenMessage, UInt16 canId)
        {
            byte serverCommandSpecifier = (byte)ServerCommandSpecifier.InitiateUploadResponse;
            byte numberOfBytesNotContainingData = 0;
            byte transferType = 0;
            byte sizeIndicator = 1;
            byte byte0 = (byte)((serverCommandSpecifier << 5) | (numberOfBytesNotContainingData << 2) | (transferType << 1) | sizeIndicator);
            byte byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
            byte byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
            byte byte3 = CanOpenValueObject.SubIndex;
            byte byte4 = (byte)(ByteArray.Count & 0x000000FF);
            byte byte5 = (byte)((ByteArray.Count & 0x0000FF00) >> 8);
            byte byte6 = (byte)((ByteArray.Count & 0x00FF0000) >> 16);
            byte byte7 = (byte)((ByteArray.Count & 0xFF000000) >> 24);
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            _numberOfUploadedBytes = 0;
            _toggleBit = 0;
        }

        /// <summary>
        /// Processes the received message for a client side segmented transfer
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideSegmentedTransfer(CanOpenMessage receivedCanOpenMessage)
        {
            // check node id
            UInt16 nodeId = receivedCanOpenMessage.CanId;
            nodeId -= ServerCobId;
            if (nodeId != NodeId)
            {
                return false;
            }

            // check server command specifier
            byte serverCommandSpecifier = (byte)((receivedCanOpenMessage.Byte0 & 0xE0) >> 5);
            if (receivedCanOpenMessage.Byte0 == 0x80)
            {
                UInt16 abortIndex = (UInt16)(receivedCanOpenMessage.Byte1 + (receivedCanOpenMessage.Byte2 << 8));
                byte abortSubIndex = receivedCanOpenMessage.Byte3;
                if (abortIndex == CanOpenValueObject.Index && abortSubIndex == CanOpenValueObject.SubIndex)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    AbortCode = new CanOpenAbortCode(receivedCanOpenMessage.Byte4, receivedCanOpenMessage.Byte5, receivedCanOpenMessage.Byte6, receivedCanOpenMessage.Byte7);
                    _pendingSendMessages.Clear();
                    TransferComplete = true;
                    return true;
                }
                return false;
            }
            if (serverCommandSpecifier == (byte)ServerCommandSpecifier.InitiateUploadResponse)
            {
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                return processReceivedMessageForClientSideSegmentedTransferInitiateState(receivedCanOpenMessage);
            }
            else if (serverCommandSpecifier == (byte)ServerCommandSpecifier.UploadSegmentResponse && _numberOfBytesToBeUploaded != 0)
            {
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                return processReceivedMessageForClientSideSegmentedTransferSegmentState(receivedCanOpenMessage);
            }
            return false;
        }

        /// <summary>
        /// Processes the received message for a client side segmented transfer
        /// in the initiate state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideSegmentedTransferInitiateState(CanOpenMessage receivedCanOpenMessage)
        {
            // check index and sub index
            UInt16 index = (UInt16)(receivedCanOpenMessage.Byte1 + (receivedCanOpenMessage.Byte2 << 8));
            byte subIndex = receivedCanOpenMessage.Byte3;
            if (index != CanOpenValueObject.Index || subIndex != CanOpenValueObject.SubIndex)
            {
                return false;
            }

            // process data
            byte transferType = (byte)((receivedCanOpenMessage.Byte0 & 0x2) >> 1);
            if (transferType == 1)
            {
                return processReceivedMessageForClientSideSegmentedTransferInitiateStateExpeditedResponse(receivedCanOpenMessage);
            }
            else
            {
                return processReceivedMessageForClientSideSegmentedTransferInitiateStateSegmentedResponse(receivedCanOpenMessage);
            }
        }

        /// <summary>
        /// Processes the received message for a client side segmented transfer
        /// in the initiate state for a expedited transfer response
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideSegmentedTransferInitiateStateExpeditedResponse(CanOpenMessage receivedCanOpenMessage)
        {
            // process data
            byte sizeIndicator = (byte)(receivedCanOpenMessage.Byte0 & 0x1);
            byte numberOfBytesNotContainingData = (byte)((receivedCanOpenMessage.Byte0 & 0xC) >> 2);
            if (sizeIndicator == 0)
            {
                numberOfBytesNotContainingData = 0;
            }
            if (numberOfBytesNotContainingData < 4)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte4);
            }
            if (numberOfBytesNotContainingData < 3)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte5);
            }
            if (numberOfBytesNotContainingData < 2)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte6);
            }
            if (numberOfBytesNotContainingData < 1)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte7);
            }
            CanOpenAbortCode abortCode = null;
            if (!CanOpenValueObject.FromByteArray(ByteArray, out abortCode))
            {
                if (abortCode == null)
                {
                    abortCode = new CanOpenAbortCode(0x00, 0x00, 0x00, 0x00);
                }
                AbortCode = abortCode;
            }

            // close transfer
            _pendingSendMessages.Clear();
            TransferComplete = true;
            return true;
        }

        /// <summary>
        /// Processes the received message for a client side segmented transfer
        /// in the initiate state for a segmented transfer response
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideSegmentedTransferInitiateStateSegmentedResponse(CanOpenMessage receivedCanOpenMessage)
        {
            // process data
            byte sizeIndicator = (byte)(receivedCanOpenMessage.Byte0 & 0x1);
            _numberOfBytesToBeUploaded = (receivedCanOpenMessage.Byte4 + (receivedCanOpenMessage.Byte5 << 8) + (receivedCanOpenMessage.Byte6 << 16) + (receivedCanOpenMessage.Byte7 << 24));
            if (sizeIndicator == 0)
            {
                _numberOfBytesToBeUploaded = -1;
            }
            if (_numberOfBytesToBeUploaded == 0)
            {
                _pendingSendMessages.Clear();
                TransferComplete = true;
                return true;
            }

            // continue transfer
            UInt16 canId = ClientCobId;
            canId += NodeId;
            _pendingSendMessages.Clear();
            byte clientCommandSpecifier = (byte)ClientCommandSpecifier.UploadSegmentRequest;
            _toggleBit = 0;
            byte byte0 = (byte)((clientCommandSpecifier << 5) | (_toggleBit << 4));
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            return true;
        }

        /// <summary>
        /// Processes the received message for a client side segmented transfer
        /// in the segment state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideSegmentedTransferSegmentState(CanOpenMessage receivedCanOpenMessage)
        {
            // initialization
            UInt16 canId = ClientCobId;
            canId += NodeId;
            byte clientCommandSpecifier = 0;
            byte byte0 = 0;
            byte byte1 = 0;
            byte byte2 = 0;
            byte byte3 = 0;

            // check toggle bit
            byte toggleBit = (byte)((receivedCanOpenMessage.Byte0 & 0x10) >> 4);
            if (toggleBit != _toggleBit)
            {
                clientCommandSpecifier = (byte)ClientCommandSpecifier.AbortTransferRequest;
                byte0 = (byte)(clientCommandSpecifier << 5);
                byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                byte3 = CanOpenValueObject.SubIndex;
                _pendingSendMessages.Clear();
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x00, 0x00, 0x03, 0x05));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                AbortCode = new CanOpenAbortCode(0x00, 0x00, 0x03, 0x05);
                _isTransferCompleteAfterSending = true;
                return true;
            }
            _toggleBit = (_toggleBit == 0 ? 1 : 0);

            // process data
            byte numberOfBytesNotContainingData = (byte)((receivedCanOpenMessage.Byte0 & 0xE) >> 1);
            if (numberOfBytesNotContainingData < 7)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte1);
            }
            if (numberOfBytesNotContainingData < 6)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte2);
            }
            if (numberOfBytesNotContainingData < 5)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte3);
            }
            if (numberOfBytesNotContainingData < 4)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte4);
            }
            if (numberOfBytesNotContainingData < 3)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte5);
            }
            if (numberOfBytesNotContainingData < 2)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte6);
            }
            if (numberOfBytesNotContainingData < 1)
            {
                ByteArray.Add(receivedCanOpenMessage.Byte7);
            }

            // check whether the transfer is complete
            byte complete = (byte)(receivedCanOpenMessage.Byte0 & 0x1);
            if (complete == 1)
            {
                if (_numberOfBytesToBeUploaded >= 0)
                {
                    while (ByteArray.Count > _numberOfBytesToBeUploaded)
                    {
                        ByteArray.RemoveAt(ByteArray.Count - 1);
                    }
                    if (ByteArray.Count != _numberOfBytesToBeUploaded)
                    {
                        clientCommandSpecifier = (byte)ServerCommandSpecifier.AbortTransferRequest;
                        byte0 = (byte)(clientCommandSpecifier << 5);
                        byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                        byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                        byte3 = CanOpenValueObject.SubIndex;
                        _pendingSendMessages.Clear();
                        _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x00, 0x00, 0x00, 0x80));
                        CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                        AbortCode = new CanOpenAbortCode(0x00, 0x00, 0x00, 0x80);
                        _isTransferCompleteAfterSending = true;
                        return true;
                    }
                }
                CanOpenAbortCode abortCode = null;
                if (!CanOpenValueObject.FromByteArray(ByteArray, out abortCode))
                {
                    if (abortCode == null)
                    {
                        abortCode = new CanOpenAbortCode(0x00, 0x00, 0x00, 0x00);
                    }
                    AbortCode = abortCode;
                }
                _pendingSendMessages.Clear();
                TransferComplete = true;
                return true;
            }

            // continue transfer
            _pendingSendMessages.Clear();
            clientCommandSpecifier = (byte)ClientCommandSpecifier.UploadSegmentRequest;
            byte0 = (byte)((clientCommandSpecifier << 5) | (_toggleBit << 4));
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            return true;
        }

        /// <summary>
        /// Processes the received message for a server side segmented transfer
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForServerSideSegmentedTransfer(CanOpenMessage receivedCanOpenMessage)
        {
            // initialization
            byte serverCommandSpecifier = 0;
            byte byte0 = 0;
            byte byte1 = 0;
            byte byte2 = 0;
            byte byte3 = 0;
            UInt16 canId = ServerCobId;
            canId += NodeId;

            // check node id
            UInt16 nodeId = receivedCanOpenMessage.CanId;
            nodeId -= ClientCobId;
            if (nodeId != NodeId)
            {
                return false;
            }

            // check client command specifier
            byte clientCommandSpecifier = (byte)((receivedCanOpenMessage.Byte0 & 0xE0) >> 5);
            if (receivedCanOpenMessage.Byte0 == 0x80)
            {
                UInt16 abortIndex = (UInt16)(receivedCanOpenMessage.Byte1 + (receivedCanOpenMessage.Byte2 << 8));
                byte abortSubIndex = receivedCanOpenMessage.Byte3;
                if (abortIndex == CanOpenValueObject.Index && abortSubIndex == CanOpenValueObject.SubIndex)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    AbortCode = new CanOpenAbortCode(receivedCanOpenMessage.Byte4, receivedCanOpenMessage.Byte5, receivedCanOpenMessage.Byte6, receivedCanOpenMessage.Byte7);
                    _pendingSendMessages.Clear();
                    TransferComplete = true;
                    return true;
                }
                return false;
            }
            if (clientCommandSpecifier != (byte)ClientCommandSpecifier.UploadSegmentRequest)
            {
                return false;
            }

            // check toggle bit
            byte toggleBit = (byte)((receivedCanOpenMessage.Byte0 & 0x10) >> 4);
            if (toggleBit != _toggleBit)
            {
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                serverCommandSpecifier = (byte)ServerCommandSpecifier.AbortTransferRequest;
                byte0 = (byte)(serverCommandSpecifier << 5);
                byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                byte3 = CanOpenValueObject.SubIndex;
                _pendingSendMessages.Clear();
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x00, 0x00, 0x03, 0x05));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                AbortCode = new CanOpenAbortCode(0x00, 0x00, 0x03, 0x05);
                _isTransferCompleteAfterSending = true;
                return true;
            }

            // continue segmented transfer
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
            _pendingSendMessages.Clear();
            serverCommandSpecifier = (byte)ServerCommandSpecifier.UploadSegmentResponse;
            byte complete = (byte)(_numberOfUploadedBytes + 7 >= ByteArray.Count ? 1 : 0);
            byte numberOfBytesNotContainingData = (byte)(complete == 0 ? 0 : (_numberOfUploadedBytes + 7 - ByteArray.Count));
            byte0 = (byte)((serverCommandSpecifier << 5) | (_toggleBit << 4) | (numberOfBytesNotContainingData << 1) | complete);
            byte1 = (byte)(ByteArray.Count > _numberOfUploadedBytes ? ByteArray[_numberOfUploadedBytes] : 0);
            byte2 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 1) ? ByteArray[_numberOfUploadedBytes + 1] : 0);
            byte3 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 2) ? ByteArray[_numberOfUploadedBytes + 2] : 0);
            byte byte4 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 3) ? ByteArray[_numberOfUploadedBytes + 3] : 0);
            byte byte5 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 4) ? ByteArray[_numberOfUploadedBytes + 4] : 0);
            byte byte6 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 5) ? ByteArray[_numberOfUploadedBytes + 5] : 0);
            byte byte7 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 6) ? ByteArray[_numberOfUploadedBytes + 6] : 0);
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            _numberOfUploadedBytes = _numberOfUploadedBytes + 7 - numberOfBytesNotContainingData;
            if (complete == 1)
            {
                _isTransferCompleteAfterSending = true;
            }
            _toggleBit = (_toggleBit == 0 ? 1 : 0);
            return true;
        }

        /// <summary>
        /// Starts a block transfer
        /// </summary>
        private void startBlockTransfer()
        {
            UInt16 canId = ClientCobId;
            canId += NodeId;
            byte clientCommandSpecifier = (byte)ClientCommandSpecifier.BlockUpload;
            byte clientCheckSumSupport = (byte)(_isCheckSumSupported ? 1 : 0);
            byte clientSubCommand = (byte)ClientSubCommand.InitiateRequest;
            byte byte0 = (byte)((clientCommandSpecifier << 5) | (clientCheckSumSupport << 2) | clientSubCommand);
            byte byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
            byte byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
            byte byte3 = CanOpenValueObject.SubIndex;
            byte byte4 = BlockSize;
            byte byte5 = ProtocolSwitchThreshold;
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, byte4, byte5, 0x00, 0x00));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            _nextBlockUploadClientSubCommand = (byte)ClientSubCommand.StartUpload;
        }

        /// <summary>
        /// Initializes a server side upload block transfer by the given
        /// parameter
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <param name="canId">
        /// CAN-ID
        /// </param>
        private void initializeServerSideUploadBlockTransfer(CanOpenMessage receivedCanOpenMessage, UInt16 canId)
        {
            // initialization
            _clientCheckSumSupport = ((receivedCanOpenMessage.Byte0 & 0x4) == 0 ? false : true);
            BlockSize = Math.Min(receivedCanOpenMessage.Byte4, (byte)127);

            // start transfer
            byte serverCommandSpecifier = (byte)ServerCommandSpecifier.BlockUpload;
            byte serverCheckSumSupport = (byte)(_isCheckSumSupported ? 1 : 0);
            byte sizeIndicator = 1;
            byte serverSubCommand = (byte)ServerSubCommand.InitiateResponse;
            byte byte0 = (byte)((serverCommandSpecifier << 5) | (serverCheckSumSupport << 2) | (sizeIndicator << 1) | serverSubCommand);
            byte byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
            byte byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
            byte byte3 = CanOpenValueObject.SubIndex;
            byte byte4 = (byte)(ByteArray.Count & 0x000000FF);
            byte byte5 = (byte)((ByteArray.Count & 0x0000FF00) >> 8);
            byte byte6 = (byte)((ByteArray.Count & 0x00FF0000) >> 16);
            byte byte7 = (byte)((ByteArray.Count & 0xFF000000) >> 24);
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            _numberOfUploadedBytes = 0;
            _blockTransferEndSend = false;
        }

        /// <summary>
        /// Processes the received message for a client side block transfer
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideBlockTransfer(CanOpenMessage receivedCanOpenMessage)
        {
            // check node id
            UInt16 nodeId = receivedCanOpenMessage.CanId;
            nodeId -= ServerCobId;
            if (nodeId != NodeId)
            {
                return false;
            }

            // check server command specifier
            byte serverCommandSpecifier = (byte)((receivedCanOpenMessage.Byte0 & 0xE0) >> 5);
            if (receivedCanOpenMessage.Byte0 == 0x80)
            {
                UInt16 abortIndex = (UInt16)(receivedCanOpenMessage.Byte1 + (receivedCanOpenMessage.Byte2 << 8));
                byte abortSubIndex = receivedCanOpenMessage.Byte3;
                if (abortIndex == CanOpenValueObject.Index && abortSubIndex == CanOpenValueObject.SubIndex)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    AbortCode = new CanOpenAbortCode(receivedCanOpenMessage.Byte4, receivedCanOpenMessage.Byte5, receivedCanOpenMessage.Byte6, receivedCanOpenMessage.Byte7);
                    _pendingSendMessages.Clear();
                    TransferComplete = true;
                    return true;
                }
                return false;
            }

            // process message
            if (_nextBlockUploadClientSubCommand == (byte)ClientSubCommand.StartUpload)
            {
                if (serverCommandSpecifier == (byte)ServerCommandSpecifier.InitiateUploadResponse)
                {
                    // -> protocol switch occured
                    _transferType = ServiceDataObjectTransferType.ClientSideSegmentedTransfer;
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    return processReceivedMessageForClientSideSegmentedTransfer(receivedCanOpenMessage);
                }
                if (serverCommandSpecifier == (byte)ServerCommandSpecifier.BlockUpload)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    return processReceivedMessageForClientSideBlockTransferInitiateState(receivedCanOpenMessage);
                }
            }
            else if (_nextBlockUploadClientSubCommand == (byte)ClientSubCommand.BlockUploadResponse)
            {
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                return processReceivedMessageForClientSideBlockTransferSubBlockState(receivedCanOpenMessage);
            }
            else if (_nextBlockUploadClientSubCommand == (byte)ClientSubCommand.EndBlockRequest)
            {
                if (serverCommandSpecifier == (byte)ServerCommandSpecifier.BlockUpload)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    return processReceivedMessageForClientSideBlockTransferEndState(receivedCanOpenMessage);
                }
            }
            return false;
        }

        /// <summary>
        /// Processes the received message for a client side block transfer in
        /// the initiate state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideBlockTransferInitiateState(CanOpenMessage receivedCanOpenMessage)
        {
            // check server sub command
            byte serverSubCommand = (byte)(receivedCanOpenMessage.Byte0 & 0x1);
            if (serverSubCommand != (byte)ServerSubCommand.InitiateResponse)
            {
                return false;
            }

            // check index and sub index
            UInt16 index = (UInt16)(receivedCanOpenMessage.Byte1 + (receivedCanOpenMessage.Byte2 << 8));
            byte subIndex = receivedCanOpenMessage.Byte3;
            if (index != CanOpenValueObject.Index || subIndex != CanOpenValueObject.SubIndex)
            {
                return false;
            }

            // process data
            _serverCheckSumSupport = (((receivedCanOpenMessage.Byte0 & 0x4) >> 2) == 0 ? false : true);
            byte sizeIndicator = (byte)((receivedCanOpenMessage.Byte0 & 0x2) >> 1);
            _numberOfBytesToBeUploaded = (receivedCanOpenMessage.Byte4 + (receivedCanOpenMessage.Byte5 << 8) + (receivedCanOpenMessage.Byte6 << 16) + (receivedCanOpenMessage.Byte7 << 24));
            if (sizeIndicator == 0)
            {
                _numberOfBytesToBeUploaded = -1;
            }
            if (_numberOfBytesToBeUploaded == 0)
            {
                _pendingSendMessages.Clear();
                TransferComplete = true;
                return true;
            }

            // continue transfer
            UInt16 canId = ClientCobId;
            canId += NodeId;
            _pendingSendMessages.Clear();
            byte clientCommandSpecifier = (byte)ClientCommandSpecifier.BlockUpload;
            byte clientSubCommand = (byte)ClientSubCommand.StartUpload;
            byte byte0 = (byte)((clientCommandSpecifier << 5) | clientSubCommand);
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            _nextBlockUploadClientSubCommand = (byte)ClientSubCommand.BlockUploadResponse;
            _sequenceNumber = 1;
            return true;
        }

        /// <summary>
        /// Processes the received message for a client side block transfer in
        /// the sub block state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideBlockTransferSubBlockState(CanOpenMessage receivedCanOpenMessage)
        {
            // initialization
            UInt16 canId = ClientCobId;
            canId += NodeId;
            byte clientCommandSpecifier = 0;
            byte byte0 = 0;
            byte byte1 = 0;
            byte byte2 = 0;
            byte byte3 = 0;

            // check sequence number
            byte sequenceNumber = (byte)(receivedCanOpenMessage.Byte0 & 0x7F);
            if (sequenceNumber != _sequenceNumber)
            {
                clientCommandSpecifier = (byte)ClientCommandSpecifier.AbortTransferRequest;
                byte0 = (byte)(clientCommandSpecifier << 5);
                byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                byte3 = CanOpenValueObject.SubIndex;
                _pendingSendMessages.Clear();
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x03, 0x00, 0x04, 0x05));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                AbortCode = new CanOpenAbortCode(0x03, 0x00, 0x04, 0x05);
                _isTransferCompleteAfterSending = true;
                return true;
            }

            // process data
            ByteArray.Add(receivedCanOpenMessage.Byte1);
            ByteArray.Add(receivedCanOpenMessage.Byte2);
            ByteArray.Add(receivedCanOpenMessage.Byte3);
            ByteArray.Add(receivedCanOpenMessage.Byte4);
            ByteArray.Add(receivedCanOpenMessage.Byte5);
            ByteArray.Add(receivedCanOpenMessage.Byte6);
            ByteArray.Add(receivedCanOpenMessage.Byte7);

            // check whether transfer is complete
            byte complete = (byte)((receivedCanOpenMessage.Byte0 & 0x80) >> 7);
            if (complete == 1)
            {
                _nextBlockUploadClientSubCommand = (byte)ClientSubCommand.EndBlockRequest;
            }

            // check whether continue the transfer
            if (_sequenceNumber == BlockSize || complete == 1)
            {
                _pendingSendMessages.Clear();
                clientCommandSpecifier = (byte)ClientCommandSpecifier.BlockUpload;
                byte clientSubCommand = (byte)ClientSubCommand.BlockUploadResponse;
                byte0 = (byte)((clientCommandSpecifier << 5) | clientSubCommand);
                byte1 = (byte)_sequenceNumber;
                byte2 = BlockSize;
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, 0x00, 0x00, 0x00, 0x00, 0x00));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                _sequenceNumber = 0;
            }
            _sequenceNumber += 1;
            return true;
        }

        /// <summary>
        /// Processes the received message for a client side block transfer in
        /// the end state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForClientSideBlockTransferEndState(CanOpenMessage receivedCanOpenMessage)
        {
            // initialization
            UInt16 canId = ClientCobId;
            canId += NodeId;
            byte clientCommandSpecifier = 0;
            byte byte0 = 0;
            byte byte1 = 0;
            byte byte2 = 0;
            byte byte3 = 0;

            // check server sub command
            byte serverSubCommand = (byte)(receivedCanOpenMessage.Byte0 & 0x3);
            if (serverSubCommand != (byte)ServerSubCommand.EndBlockResponse)
            {
                return false;
            }

            // read data
            byte numberOfBytesNotContainingData = (byte)((receivedCanOpenMessage.Byte0 & 0x1C) >> 2);
            for (int i = 1; i <= numberOfBytesNotContainingData; i++)
            {
                if (ByteArray.Count > 0)
                {
                    ByteArray.RemoveAt(ByteArray.Count - 1);
                }
            }
            if (_numberOfBytesToBeUploaded >= 0)
            {
                while (ByteArray.Count > _numberOfBytesToBeUploaded)
                {
                    ByteArray.RemoveAt(ByteArray.Count - 1);
                }
                if (ByteArray.Count != _numberOfBytesToBeUploaded)
                {
                    clientCommandSpecifier = (byte)ServerCommandSpecifier.AbortTransferRequest;
                    byte0 = (byte)(clientCommandSpecifier << 5);
                    byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                    byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                    byte3 = CanOpenValueObject.SubIndex;
                    _pendingSendMessages.Clear();
                    _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x02, 0x00, 0x04, 0x05));
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                    AbortCode = new CanOpenAbortCode(0x02, 0x00, 0x04, 0x05);
                    _isTransferCompleteAfterSending = true;
                    return true;
                }
            }

            // check check sum
            if (_serverCheckSumSupport && _isCheckSumSupported)
            {
                UInt16 serverCheckSum = (UInt16)(receivedCanOpenMessage.Byte1 + (receivedCanOpenMessage.Byte2 << 8));
                UInt16? clientCheckSum = createCyclicRedundancyChecksum();
                if (clientCheckSum == null || clientCheckSum.Value != serverCheckSum)
                {
                    clientCommandSpecifier = (byte)ClientCommandSpecifier.AbortTransferRequest;
                    byte0 = (byte)(clientCommandSpecifier << 5);
                    byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                    byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                    byte3 = CanOpenValueObject.SubIndex;
                    _pendingSendMessages.Clear();
                    _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x04, 0x00, 0x04, 0x05));
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                    AbortCode = new CanOpenAbortCode(0x04, 0x00, 0x04, 0x05);
                    _isTransferCompleteAfterSending = true;
                    return true;
                }
            }

            // process data
            CanOpenAbortCode abortCode = null;
            if (_transferType == ServiceDataObjectTransferType.ClientSideBlockTransfer)
            {
                CanOpenValueObject.ByteOrder = CanOpenByteOrder.BigEndian;
            }
            else
            {
                CanOpenValueObject.ByteOrder = CanOpenByteOrder.LittleEndian;
            }
            if (!CanOpenValueObject.FromByteArray(ByteArray, out abortCode))
            {
                CanOpenValueObject.ByteOrder = CanOpenByteOrder.LittleEndian;
                if (abortCode == null)
                {
                    abortCode = new CanOpenAbortCode(0x00, 0x00, 0x00, 0x00);
                }
                clientCommandSpecifier = (byte)ClientCommandSpecifier.AbortTransferRequest;
                byte0 = (byte)(clientCommandSpecifier << 5);
                byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                byte3 = CanOpenValueObject.SubIndex;
                _pendingSendMessages.Clear();
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, abortCode.Byte4, abortCode.Byte5, abortCode.Byte6, abortCode.Byte7));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                AbortCode = abortCode;
                _isTransferCompleteAfterSending = true;
                return true;
            }
            CanOpenValueObject.ByteOrder = CanOpenByteOrder.LittleEndian;

            // end block transfer
            _pendingSendMessages.Clear();
            clientCommandSpecifier = (byte)ClientCommandSpecifier.BlockUpload;
            byte clientSubCommand = (byte)ClientSubCommand.EndBlockRequest;
            byte0 = (byte)((clientCommandSpecifier << 5) | clientSubCommand);
            _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00));
            CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
            _isTransferCompleteAfterSending = true;
            return true;
        }

        /// <summary>
        /// Processes the received message for a server side block transfer
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForServerSideBlockTransfer(CanOpenMessage receivedCanOpenMessage)
        {
            // check node id
            UInt16 nodeId = receivedCanOpenMessage.CanId;
            nodeId -= ClientCobId;
            if (nodeId != NodeId)
            {
                return false;
            }

            // check client command specifier
            byte clientCommandSpecifier = (byte)((receivedCanOpenMessage.Byte0 & 0xE0) >> 5);
            if (receivedCanOpenMessage.Byte0 == 0x80)
            {
                UInt16 abortIndex = (UInt16)(receivedCanOpenMessage.Byte1 + (receivedCanOpenMessage.Byte2 << 8));
                byte abortSubIndex = receivedCanOpenMessage.Byte3;
                if (abortIndex == CanOpenValueObject.Index && abortSubIndex == CanOpenValueObject.SubIndex)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    AbortCode = new CanOpenAbortCode(receivedCanOpenMessage.Byte4, receivedCanOpenMessage.Byte5, receivedCanOpenMessage.Byte6, receivedCanOpenMessage.Byte7);
                    _pendingSendMessages.Clear();
                    TransferComplete = true;
                    return true;
                }
                return false;
            }
            if (clientCommandSpecifier != (byte)ClientCommandSpecifier.BlockUpload)
            {
                return false;
            }

            // process message
            if (!_blockTransferEndSend)
            {
                byte clientSubCommand = (byte)(receivedCanOpenMessage.Byte0 & 0x3);
                if (clientSubCommand == (byte)ClientSubCommand.StartUpload)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    return processReceivedMessageForServerSideBlockTransferSubBlockStartState(receivedCanOpenMessage);
                }
                else if (clientSubCommand == (byte)ClientSubCommand.BlockUploadResponse)
                {
                    CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                    return processReceivedMessageForServerSideBlockTransferSubBlockContinueState(receivedCanOpenMessage);
                }
            }
            else
            {
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(receivedCanOpenMessage);
                return processReceivedMessageForServerSideBlockTransferEndState(receivedCanOpenMessage);
            }
            return false;
        }

        /// <summary>
        /// Processes the received message for a server side block transfer in
        /// the sub block start state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForServerSideBlockTransferSubBlockStartState(CanOpenMessage receivedCanOpenMessage)
        {
            _pendingSendMessages.Clear();
            createPendingSendMessagesForServerSideBlockTransfer();
            return true;
        }

        /// <summary>
        /// Processes the received message for a server side block transfer in
        /// the sub block continue state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForServerSideBlockTransferSubBlockContinueState(CanOpenMessage receivedCanOpenMessage)
        {
            // initialization
            byte serverCommandSpecifier = 0;
            byte byte0 = 0;
            byte byte1 = 0;
            byte byte2 = 0;
            byte byte3 = 0;
            UInt16 canId = ServerCobId;
            canId += NodeId;

            // initialize block size
            BlockSize = Math.Min(receivedCanOpenMessage.Byte4, (byte)127);

            // check acknowledge (and re-transfer segments if necessary)
            if (receivedCanOpenMessage.Byte1 == 0)
            {
                _numberOfUploadedBytes = Math.Max(_numberOfUploadedBytes - (BlockSize * 7), 0);
                _numberOfBytesNotContainingData = 0;
                _pendingSendMessages.Clear();
                createPendingSendMessagesForServerSideBlockTransfer();
                return true;
            }
            else if (receivedCanOpenMessage.Byte1 != _sequenceNumber)
            {
                serverCommandSpecifier = (byte)ServerCommandSpecifier.AbortTransferRequest;
                byte0 = (byte)(serverCommandSpecifier << 5);
                byte1 = (byte)(CanOpenValueObject.Index & 0x00FF);
                byte2 = (byte)((CanOpenValueObject.Index & 0xFF00) >> 8);
                byte3 = CanOpenValueObject.SubIndex;
                _pendingSendMessages.Clear();
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, 0x03, 0x00, 0x04, 0x05));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                AbortCode = new CanOpenAbortCode(0x03, 0x00, 0x04, 0x05);
                _isTransferCompleteAfterSending = true;
                return true;
            }

            // check whether to end the transfer
            if (_numberOfUploadedBytes == ByteArray.Count)
            {
                serverCommandSpecifier = (byte)ServerCommandSpecifier.BlockUpload;
                byte serverSubCommand = (byte)ServerSubCommand.EndBlockResponse;
                UInt16? serverCheckSum = createCyclicRedundancyChecksum();
                byte0 = (byte)((serverCommandSpecifier << 5) | (_numberOfBytesNotContainingData << 2) | serverSubCommand);
                byte1 = (byte)(_clientCheckSumSupport && serverCheckSum != null ? (serverCheckSum.Value & 0x00FF) : 0);
                byte2 = (byte)(_clientCheckSumSupport && serverCheckSum != null ? ((serverCheckSum.Value & 0xFF00) >> 8) : 0);
                _pendingSendMessages.Clear();
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, 0x00, 0x00, 0x00, 0x00, 0x00));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                _blockTransferEndSend = true;
                return true;
            }

            // continue transfer
            _pendingSendMessages.Clear();
            createPendingSendMessagesForServerSideBlockTransfer();
            return true;
        }

        /// <summary>
        /// Processes the received message for a server side block transfer in
        /// the end state
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        private bool processReceivedMessageForServerSideBlockTransferEndState(CanOpenMessage receivedCanOpenMessage)
        {
            // check client sub command
            byte clientSubCommand = (byte)(receivedCanOpenMessage.Byte0 & 0x1);
            if (clientSubCommand != (byte)ClientSubCommand.EndBlockRequest)
            {
                return false;
            }

            // end transfer
            _pendingSendMessages.Clear();
            TransferComplete = true;
            return true;
        }

        /// <summary>
        /// Creates the pending send messaged for a server side block transfer
        /// </summary>
        private void createPendingSendMessagesForServerSideBlockTransfer()
        {
            UInt16 canId = ServerCobId;
            canId += NodeId;
            for (_sequenceNumber = 1; _sequenceNumber <= BlockSize; _sequenceNumber++)
            {
                byte complete = (byte)(_numberOfUploadedBytes + 7 >= ByteArray.Count ? 1 : 0);
                byte byte0 = (byte)((complete << 7) | _sequenceNumber);
                byte byte1 = (byte)(ByteArray.Count > _numberOfUploadedBytes ? ByteArray[_numberOfUploadedBytes] : 0);
                byte byte2 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 1) ? ByteArray[_numberOfUploadedBytes + 1] : 0);
                byte byte3 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 2) ? ByteArray[_numberOfUploadedBytes + 2] : 0);
                byte byte4 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 3) ? ByteArray[_numberOfUploadedBytes + 3] : 0);
                byte byte5 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 4) ? ByteArray[_numberOfUploadedBytes + 4] : 0);
                byte byte6 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 5) ? ByteArray[_numberOfUploadedBytes + 5] : 0);
                byte byte7 = (byte)(ByteArray.Count > (_numberOfUploadedBytes + 6) ? ByteArray[_numberOfUploadedBytes + 6] : 0);
                _pendingSendMessages.Add(new CanOpenMessage(canId, byte0, byte1, byte2, byte3, byte4, byte5, byte6, byte7));
                CanOpenValueObject.LastServiceDataObjectUploadSequence.Add(_pendingSendMessages[_pendingSendMessages.Count - 1]);
                if (complete == 1)
                {
                    _numberOfBytesNotContainingData = (_numberOfUploadedBytes + 7 - ByteArray.Count);
                    _numberOfUploadedBytes = ByteArray.Count;
                    break;
                }
                else
                {
                    _numberOfUploadedBytes += 7;
                }
            }
        }

        /// <summary>
        /// Creates the CRC (cyclic redundancy checksum)
        /// </summary>
        /// <returns>
        /// CRC of null if the checksum couldn't be created
        /// </returns>
        private UInt16? createCyclicRedundancyChecksum()
        {
            // -->> TODO: implement CRC
            return null;
            // <<-- TODO
        }


        #region ITransfer implementation
        /// <summary>
        /// Gets a flag indicating whether the transfer is complete (after
        /// sending the pending send messages)
        /// </summary>
        public bool TransferComplete
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the abort code (null for successful transfers)
        /// </summary>
        public CanOpenAbortCode AbortCode
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the pending send messages
        /// </summary>
        public ReadOnlyCollection<CanOpenMessage> PendingSendMessages
        {
            get
            {
                if (_pendingSendMessagesReference == null)
                {
                    _pendingSendMessagesReference = new ReadOnlyCollection<CanOpenMessage>(_pendingSendMessages);
                }
                return _pendingSendMessagesReference;
            }
        }


        /// <summary>
        /// Starts the transfer
        /// </summary>
        public void Start()
        {
            if (_transferType == ServiceDataObjectTransferType.ClientSideExpeditedTransfer ||
                _transferType == ServiceDataObjectTransferType.ClientSideSegmentedTransfer ||
                _transferType == ServiceDataObjectTransferType.ClientSideBlockTransfer)
            {
                startClientSideTransfer();
            }
            else if (_transferType == ServiceDataObjectTransferType.ServerSideExpeditedTransfer ||
                _transferType == ServiceDataObjectTransferType.ServerSideSegmentedTransfer ||
                _transferType == ServiceDataObjectTransferType.ServerSideBlockTransfer)
            {
                startServerSideTransfer();
            }
        }

        /// <summary>
        /// Times out the transfer
        /// </summary>
        public void TimeOut()
        {
            AbortCode = new CanOpenAbortCode(0x00, 0x00, 0x04, 0x05);
        }

        /// <summary>
        /// Finalizes the transfer
        /// </summary>
        public void FinalizeTransfer()
        {
            Thread finalizeThread = new Thread(new ThreadStart(() =>
            {
                if (CanOpenValueObject != null)
                {
                    CanOpenValueObject.IsValueChangedEventRaisingPaused = false;
                }
            }));
            finalizeThread.Start();
        }

        /// <summary>
        /// Continues the transfer after sending messages
        /// </summary>
        public void ContinueAfterSendingMessages()
        {
            _pendingSendMessages.Clear();
            if (_isTransferCompleteAfterSending)
            {
                TransferComplete = true;
            }
        }

        /// <summary>
        /// Processes the received message
        /// </summary>
        /// <param name="receivedCanOpenMessage">
        /// Received can open message
        /// </param>
        /// <returns>
        /// True, if the given can open message was related to this transfer,
        /// false otherwise
        /// </returns>
        public bool ProcessReceivedMessage(CanOpenMessage receivedCanOpenMessage)
        {
            if (receivedCanOpenMessage == null)
            {
                return false;
            }
            if (TransferComplete)
            {
                return false;
            }
            switch (_transferType)
            {
                case ServiceDataObjectTransferType.ClientSideSegmentedTransfer:
                    return processReceivedMessageForClientSideSegmentedTransfer(receivedCanOpenMessage);
                case ServiceDataObjectTransferType.ClientSideBlockTransfer:
                    return processReceivedMessageForClientSideBlockTransfer(receivedCanOpenMessage);
                case ServiceDataObjectTransferType.ServerSideSegmentedTransfer:
                    return processReceivedMessageForServerSideSegmentedTransfer(receivedCanOpenMessage);
                case ServiceDataObjectTransferType.ServerSideBlockTransfer:
                    return processReceivedMessageForServerSideBlockTransfer(receivedCanOpenMessage);
            }
            return false;
        }
        #endregion ITransfer implementation

    }

}
